问题
静态工厂和构造器有个共同特征,就是无法支持多个可选参数。如果构建一个多个可选参数的对象,常用的方式有重叠构造器和JavaBean的方式:
重叠构造器
提供一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个构造器有两个可选参数,依次类推,直到最后一个构造器包含所有的可选参数。
缺点:重叠构造器模式在有许多可选参数的时候,代码可读性很差,而且调用者想知道每个参数的意思,甚至需要深入到源码中去看每个参数的意义。如果多个参数的类型相同,调用者颠倒了参数的位置,编译的时候也不会报错,就会产生一些微妙的错误。
JavaBean模式
通过一个无参构造器创建对象,然后调用setter方法来设置每个必要的参数。这样创建实例很容易,产生的代码读起来也比较容易,因为通过setter的方法名能知道设置的参数是哪一个。
缺点:构造过程被分到了几个调用之中,在构造过程中无法保证JavaBean的一致性,容易出现线程安全的问题。
那么,当构建对象的时候有多个可选参数的时候,应该如何处理?
答案
在所构建的对象中,有多个可选参数的话,可采用Builder方式来构建对象:
public class User { private String userName; private int age; //可选参数 private String address; private String phoneNumber; public static class Builder{ private String userName; private int age; //可选参数的默认值 private String address=""; private String phoneNumber=""; public Builder(String userName, int age){ this.userName = userName; this.age = age; } public Builder address(String address){ this.address = address; return this; } public Builder phoneNumber(String phoneNumber){ this.phoneNumber = phoneNumber; return this; } public User build(){ return new User(this); } } private User(Builder builder){ this.userName = builder.userName; this.age = builder.age; this.address = builder.address; this.phoneNumber = builder.phoneNumber; } } //调用方式 User user = new User.Builder("test",18).address("test").phoneNumber("test").build();
Builder模式可以对其参数加以约束,build方法可以检验这些约束条件,将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对它们进行检验,如果违反了任何约束,build方法就应该抛出IllegalStateException。Builder模式十分灵活,可以利用单个Builder构建多个对象。唯一的不足在于:为了创建对象必须先创建一个中间对象,builder模式还使得构造的过程更加冗长。
结论
如果类的构造器或者静态工厂中具有多个参数时,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候,与使用传统的重叠构造器模式项目,使用Builder模式代码可读性更强,也比JavaBean的方式更加安全。